﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Json;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;



namespace ScrumFactory.Services.Logic {

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [Export(typeof(IAuthorizationService))]
    public class AuthorizationService : IAuthorizationService {

        static Dictionary<string, AuthorizationInfo> authorizedTokens = new Dictionary<string,AuthorizationInfo>();

        [Import(typeof(ITeamService))]
        private ITeamService teamService { get; set; }

        [Import(typeof(IProjectsService))]
        private IProjectsService projectsService { get; set; }

        [ImportMany(typeof(Services.AuthProviders.IOAuthServerTokenValidator))]
        private IEnumerable<Services.AuthProviders.IOAuthServerTokenValidator> authorizationProviders { get; set; }

        public string ServerVersion {
            get {
                return "2.50";
            }
        }

        public void VerifyTargetVersion() {

            string targetVersion = WebOperationContext.Current.IncomingRequest.Headers["UserAgent"];
            if(targetVersion==null)
                throw new WebFaultException<string>(ServerVersion, System.Net.HttpStatusCode.HttpVersionNotSupported);
            if (targetVersion == "SFClient-DEV")
                return;
            
            targetVersion = GetMajorVersion(targetVersion);

            if (targetVersion != ServerVersion)
                throw new WebFaultException<string>(ServerVersion, System.Net.HttpStatusCode.HttpVersionNotSupported);
        }

        private string GetMajorVersion(string version) {
            string[] parts = version.Replace("SFClient-", "").Split('.');
            return parts[0] + "." + parts[1];
        }

        [WebInvoke(Method = "POST", UriTemplate = "ValidTokens/{providerName}/", RequestFormat = WebMessageFormat.Json)]
        public MemberProfile SignInMember(string providerName, string token) {

            VerifyTargetVersion();

            // if the token is not authenticated yet, validate it
            AuthorizationInfo authInfo;
            MemberProfile memberInfo = null;
            if (!authorizedTokens.ContainsKey(token))                
                memberInfo = CreateAuthorizationForToken(providerName, token);
                        
            // get authInfo
            authInfo = authorizedTokens[token];

            // token was not valid
            if (authInfo == null)
                throw new WebFaultException<string>("Could not validate token", System.Net.HttpStatusCode.Unauthorized);

         
            // gets the user, or create anew one if was first time
            MemberProfile member = GetOrCreateMember(authInfo.MemberUId, memberInfo);
            if (member == null)
                return null;
     

            return member;
        }
        
    

        public void VerifyRequestAuthorizationToken() {

            var token = SignedMemberToken;

            if (!authorizedTokens.ContainsKey(token))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

        }

        private void GetTokenFromRequest(out string providerName, out string token) {
            string auth = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
            if (auth == null)
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            string[] parts = auth.Split('=');
            if (parts.Length < 2)
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            providerName = parts[0].Replace("auth","").Trim();
            token = auth.Replace(providerName + " auth=", "").Trim();

        }


        [WebInvoke(Method = "DELETE", UriTemplate = "ValidTokens/{providerName}/", RequestFormat = WebMessageFormat.Json)]
        public void SignOutMember(string providerName) {
         
            // if is not logged get out
            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                return;

            AuthorizationInfo info = authorizedTokens[SignedMemberToken];
            if (info == null || info.ProviderName != providerName)
                throw new WebFaultException(System.Net.HttpStatusCode.BadRequest);

            authorizedTokens.Remove(SignedMemberToken);
        }



    

        public MemberProfile SignedMemberProfile {
            get {
                string token = SignedMemberToken;
                if (!authorizedTokens.ContainsKey(token))
                    throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

                AuthorizationInfo info = authorizedTokens[SignedMemberToken];                
                return teamService.GetMember(info.MemberUId);
            }
        }

        public string SignedMemberToken {
            get {
                string provider;
                string token;
                GetTokenFromRequest(out provider, out token);
                return token;
            }
        }

        private Services.AuthProviders.IOAuthServerTokenValidator GetAuthorizationProvider(string providerName) {
            return authorizationProviders.SingleOrDefault(p => p.ProviderName == providerName);
        }

        private string DefaultCompanyName {
            get {
                return System.Configuration.ConfigurationManager.AppSettings["DefaultCompanyName"];
            }
        }

        private MemberProfile GetOrCreateMember(string memberUId, MemberProfile memberInfo) {

            if (String.IsNullOrEmpty(memberUId))
                throw new WebFaultException<string>("Could not get or create member. MemberUId is null", HttpStatusCode.BadRequest);
                

            MemberProfile member = teamService.GetMember(memberUId);

            // is the member does not exist, create it
            if (member == null) {

                string name = memberUId;
                string email = null;
                
                if (memberInfo != null) {
                    email = memberInfo.EmailAccount;
                    if(memberInfo.FullName!=null) name = memberInfo.FullName;
                }

                member = new MemberProfile();
                member.MemberUId = memberUId;
                member.FullName = name;
                member.EmailAccount = email;
                member.IsFactoryOwner = false;
                member.CanSeeProposalValues = false;
                member.CompanyName = DefaultCompanyName;
                member.IsActive = true;
                member.AuthorizationProvider = "-DEPRECATED-";                
                teamService.CreateMember(member);
            }
           
            return member;
                
        }

        private MemberProfile CreateAuthorizationForToken(string providerName, string token) {

            // no provider to validate the token
            Services.AuthProviders.IOAuthServerTokenValidator provider = GetAuthorizationProvider(providerName);
            if (provider == null)
                throw new WebFaultException<string>("Provider not informed", System.Net.HttpStatusCode.BadRequest);

            // not a valid token
            if (!provider.ValidateAccessToken(token))
                throw new WebFaultException<string>("Token not Valid", System.Net.HttpStatusCode.Unauthorized);

            // so the token is valid, at it the valid tokens
            AuthorizationInfo info = new AuthorizationInfo();
            info.ProviderName = providerName;
            info.MemberUId = provider.MemberInfo.MemberUId;            
            info.Token = token;
            info.IssueDate = DateTime.Now;

            authorizedTokens.Add(token.Trim().Replace("\n", ""), info);

            return provider.MemberInfo;
        }

        
        public void VerifyUser(string memberUId) {

            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            if(SignedMemberProfile.MemberUId!=memberUId)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyUserOrPermissionAtProject(string memberUId, string projectUId, PermissionSets permission) {
            if (!projectsService.MemberHasPermissionAtProject(SignedMemberProfile.MemberUId, projectUId, new PermissionSets[] {  permission })
                && SignedMemberProfile.MemberUId != memberUId)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyPermissionAtProject(string projectUId, PermissionSets permission) {
            PermissionSets[] ps = new PermissionSets[1];
            ps[0] = permission;
            VerifyPermissionAtProject(projectUId, ps);
        }

        public void VerifyPermissionAtProject(string projectUId, PermissionSets[] permissions) {

            string token = SignedMemberToken;
            if (!authorizedTokens.ContainsKey(token))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            string memberUId = authorizedTokens[token].MemberUId;
            
            bool hasPermission = projectsService.MemberHasPermissionAtProject(memberUId, projectUId, permissions);
            if(!hasPermission)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyFactoryOwner() {
            
            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            if(!SignedMemberProfile.IsFactoryOwner)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public void VerifyCanSeeProposalValues() {

            if (!authorizedTokens.ContainsKey(SignedMemberToken))
                throw new WebFaultException(System.Net.HttpStatusCode.Unauthorized);

            if (!SignedMemberProfile.CanSeeProposalValues)
                throw new WebFaultException(System.Net.HttpStatusCode.Forbidden);
        }

        public bool IsProjectScrumMaster(string projectUId) {
            return projectsService.MemberHasPermissionAtProject(
                SignedMemberProfile.MemberUId,
                projectUId,
                new PermissionSets[] { PermissionSets.SCRUM_MASTER });
        }

        protected class AuthorizationInfo {

            public string ProviderName;            
            public string Token;
            public string MemberUId;
            public DateTime IssueDate;
        }

    }

    

    
}
